#include "StdAfx.h"
#include "Square.h"
#include "Element.h"
#include "LocalPane.h"

CElement::CElement(CLocalPane* pSquarePane, CPoint firstPoint, UINT nSquareWidth, UINT nStateCount, COLORREF color, ElementState state, SpinMode mode) 
	: m_pSquarePane(pSquarePane)
	, m_pntFirstPoint(firstPoint)
    , m_nSquareWidth(nSquareWidth)
	, m_nStateCount(nStateCount)
	, m_ElementColor(color)
	, m_CurrentSpinMode(mode)
	, m_CurrentState(state)
{
}

CElement::~CElement(void)
{
}

//***************************** Draw and fill functions ****************************

void CElement::Draw()
{
	CDC* pDC = m_pSquarePane->GetPaneWnd()->GetDC();

	ASSERT(pDC);

	Draw(pDC);
}

void CElement::Draw(CDC* pDC)
{
	for (SquareListIter iter = m_listSquare.begin(); 
		 iter != m_listSquare.end(); 
		 ++iter)
	{
		iter->Draw(pDC);
	}	
}

void CElement::Draw(CDC* pDC, CPoint firstPoint, double fScale)
{
}

void CElement::Fill()
{
	CDC* pDC = m_pSquarePane->GetPaneWnd()->GetDC();

	ASSERT(pDC);

	Fill(pDC);	
}

void CElement::Fill(CDC* pDC)
{
	ASSERT(pDC);

	for (SquareListIter iter = m_listSquare.begin(); iter != m_listSquare.end(); ++iter)
	{
		iter->Fill(pDC);
	}	
}

void CElement::Fill(CDC* pDC, CPoint firstPoint, double fScale)
{
	ASSERT(pDC);

	int iXDiff = firstPoint.x - m_pntFirstPoint.x;
	int iYDiff = firstPoint.y - m_pntFirstPoint.y;

	for (SquareListIter iter = m_listSquare.begin(); 
		 iter != m_listSquare.end(); 
		 ++iter)
	{
		CPoint pntOriginal = iter->GetLeftTop();

		CPoint pntRelative;
		pntRelative.x = pntOriginal.x + iXDiff;
		pntRelative.y = pntOriginal.y + iYDiff;

		CPoint pntDest;
		pntDest.x = firstPoint.x + (pntRelative.x - firstPoint.x) * fScale;
		pntDest.y = firstPoint.y + (pntRelative.y -firstPoint.y) * fScale;

		iter->Fill(pDC, pntDest, fScale);
	}	
}

//***************************** Motion functions **********************************

bool CElement::CanSpin()
{
	SquareList tmp;

	CalcSpinSquareList(tmp);

	return m_pSquarePane->ValidateSquareList(tmp);
}

bool CElement::CanGoDown()
{
	SquareList tmp = m_listSquare;

	CalcGoDownSquareList(tmp);

	return m_pSquarePane->ValidateSquareList(tmp);
}

bool CElement::CanGoLeft()
{
	SquareList tmp = m_listSquare;

	CalcGoLeftSquareList(tmp);

	return m_pSquarePane->ValidateSquareList(tmp);
}

bool CElement::CanGoRight()
{
	SquareList tmp = m_listSquare;

	CalcGoRightSquareList(tmp);

	return m_pSquarePane->ValidateSquareList(tmp);
}

bool CElement::Spin()
{
	if (!CanSpin())
	{
		return false;
	}
	
	// Update square list
	CalcSpinSquareList(m_listSquare);

	// Update first point
	//m_pntFirstPoint = GetNextFirstPoint();

	// Update state
	m_CurrentState = GetNextState();

	return true;
}

bool CElement::GoDown(void)
{
	if (!CanGoDown())
	{
		return false;
	}

	// Update square list
	CalcGoDownSquareList(m_listSquare);
		
	// Update first point
	m_pntFirstPoint.y += m_nSquareWidth;

	return true;
}

bool CElement::GoLeft(void)
{
	if (!CanGoLeft())
	{
		return false;
	}

	// Update square list
	CalcGoLeftSquareList(m_listSquare);
		
	// Update first point
	m_pntFirstPoint.x -= m_nSquareWidth;

	return true;
}

bool CElement::GoRight(void)
{
	if (!CanGoRight())
	{
		return false;
	}

	// Update square list
	CalcGoRightSquareList(m_listSquare);
		
	// Update first point
	m_pntFirstPoint.x += m_nSquareWidth;

	return true;
}

//****************************** Attribute functions *****************************

SpinMode CElement::GetSpinMode() const
{
	return m_CurrentSpinMode;
}

void CElement::SetSpinMode(SpinMode mode)
{
	m_CurrentSpinMode = mode;
}

CPoint CElement::GetFirstPoint() const
{
    return m_pntFirstPoint;
}

void CElement::SetFirstPoint(const CPoint& pnt)
{
    int iXDiff = m_pntFirstPoint.x - pnt.x;
    int iYDiff = m_pntFirstPoint.y - pnt.y;
    
	for (SquareListIter iter = m_listSquare.begin(); iter != m_listSquare.end(); ++iter)
    {
        CPoint point = iter->GetLeftTop();
        point.x -= iXDiff;
        point.y -= iYDiff;
        
        iter->SetLeftTop(point);
    }

    m_pntFirstPoint = pnt;
}

const SquareList& CElement::GetSquareList() const
{
	return m_listSquare;
}

ElementState CElement::GetNextState() const
{
	ElementState nextState = ES_FIRST;

	UINT nStateCount = GetStateCount();

	ASSERT(nStateCount > 0 && nStateCount < 5);

	if (nStateCount == 1)
	{
		nextState = m_CurrentState;
	}
	else
	{
		switch (m_CurrentSpinMode)
		{
		case SM_CLOCK_WISE:
			{
				nextState = (ElementState)(((int)m_CurrentState + 1) % nStateCount);

				break;
			}
		case SM_COUNTER_CLOCK_WISE:
			{
				nextState = (ElementState)(((int)m_CurrentState + nStateCount - 1) % nStateCount);

				break;
			}
		default:
			break;
		}
	}

	return nextState;
}

UINT CElement::GetStateCount() const
{
	return m_nStateCount;
}

UINT CElement::GetSquareWidth() const
{
	return m_nSquareWidth;
}

void CElement::SetSquareWidth(UINT nSquareWidth)
{
    m_nSquareWidth = nSquareWidth;

    ConstructSquareList(m_CurrentState, m_pntFirstPoint, m_nSquareWidth, m_listSquare);
}

CRect CElement::GetOutlineRect() const
{
	CRect rect;

	UINT nMinLeft, nMaxRight, nMinTop, nMaxBottom;

	nMinLeft = m_listSquare.front().GetLeftTop().x;
	nMinTop = m_listSquare.front().GetLeftTop().y;
	nMaxRight = m_listSquare.front().GetRightBottom().x;
	nMaxBottom = m_listSquare.front().GetRightBottom().y;

	for (SquareListCIter iter = m_listSquare.begin(); 
		 iter != m_listSquare.end();
		 ++iter)
	{
		CPoint pntLeftTop = iter->GetLeftTop();
		CPoint pntRightBottom = iter->GetRightBottom();

		if (pntLeftTop.x <= nMinLeft)
		{
			nMinLeft = pntLeftTop.x;
		}

		if (pntLeftTop.y <= nMinTop)
		{
			nMinTop = pntLeftTop.y;
		}

		if (pntRightBottom.x >= nMaxRight)
		{
			nMaxRight = pntRightBottom.x;
		}

		if (pntRightBottom.y >= nMaxBottom)
		{
			nMaxBottom = pntRightBottom.y;
		}
	}

	rect.SetRect(nMinLeft, nMinTop, nMaxRight, nMaxBottom);

	return rect;
}

//*********************** Calculate square list functions ************************

void CElement::CalcSpinSquareList(SquareList& listSquare) const
{
	// Get next state
	ElementState nextState = GetNextState();

	// Construct square list
	ConstructSquareList(nextState, m_pntFirstPoint, m_nSquareWidth, listSquare);
}

void CElement::CalcGoDownSquareList(SquareList& listSquare) const
{
	for (SquareListIter iter = listSquare.begin(); iter != listSquare.end(); ++iter)
	{
		iter->GoDown();
	}
}

void CElement::CalcGoLeftSquareList(SquareList& listSquare) const
{
	for (SquareListIter iter = listSquare.begin(); iter != listSquare.end(); ++iter)
	{
		iter->GoLeft();
	}
}

void CElement::CalcGoRightSquareList(SquareList& listSquare) const
{
	for (SquareListIter iter = listSquare.begin(); iter != listSquare.end(); ++iter)
	{
		iter->GoRight();
	}
}
